#include "BSException/BSException.h"
#include "boost/filesystem.hpp"
#include "Directory/Directory.h"
#include "File.h"

using namespace SystemTool;
using namespace SystemTool::IO;

// Construct the file
File::File() :
	m_bIsReadMode(true),
	m_bDisposed(false)
{

}

// Destory the file
File::~File()
{
	if (!GetDisposed())
	{
		SetDisposed(true);

		Close();
	}
}

// Open a file 
bool File::Open(std::string strFilePath, OpenMode eOpenMode)
{
	if (strFilePath.empty())
	{
		return false;
	}

	try
	{
		if (eOpenMode == FILE_READ)
		{
			SetIsReadMode(true);
		}
		else
		{
			SetIsReadMode(false);
		}

		m_FileStreamer.open(strFilePath, eOpenMode);

		if (!m_FileStreamer.is_open())
		{
			return false;
		}
	}
	catch (BSException& e)
	{
		throw e;
	}
	
	return true;
}

// Read the file 
bool File::Read(char* pBuffer, int iReadSize)
{
	try
	{
		if (pBuffer == NULL)
		{
			throw BSException() << "Read buffer is null";
		}

		if (iReadSize <= 0)
		{
			throw BSException() << "Read size <= 0";
		}

		if (!m_FileStreamer.is_open())
		{
			throw BSException() << "Please open the file at first";
		}

		m_FileStreamer.read(pBuffer, iReadSize);
	}
	catch (BSException& e)
	{
		throw e;
	}

	return true;
}

// Write data to the file
bool File::Write(const char* pBuffer, int iWriteSize)
{
	try
	{
		if (pBuffer == NULL)
		{
			throw BSException() << "Write buffer is null";
		}

		if (iWriteSize <= 0)
		{
			throw BSException() << "Write size <= 0";
		}

		if (!m_FileStreamer.is_open())
		{
			throw BSException() << "Please open the file at first";
		}

		m_FileStreamer.write(pBuffer, iWriteSize);
		
		m_FileStreamer.flush();
	}
	catch (BSException & e)
	{
		throw e;
	}

	return true;
}

// Close the file
void File::Close()
{
	if (m_FileStreamer.is_open())
	{
		m_FileStreamer.close();
	}
}

// Seek the file
void File::Seek(SeekOrigin eSeekPos, int iOffset)
{
	if (m_FileStreamer.is_open())
	{
		if (GetIsReadMode())
		{
			m_FileStreamer.seekg(iOffset, eSeekPos);
		}
		else
		{
			m_FileStreamer.seekp(iOffset, eSeekPos);
		}
	}
}

// Flush the file
void File::Flush()
{
	if (m_FileStreamer.is_open())
	{
		m_FileStreamer.flush();
	}
}

// Get file size
unsigned long long File::GetFileSize(std::string strFilePath)
{
	if (strFilePath.empty())
	{
		return 0;
	}

	uintmax_t iFileSize = 0;

	try
	{
		try
		{
			boost::filesystem::path CurFilePath(strFilePath);

			iFileSize = boost::filesystem::file_size(CurFilePath);
		}
		catch (boost::filesystem::filesystem_error & e)
		{
			throw BSException() << e.code().value() << e.what();
		}
	}
	catch (BSException& e)
	{
		throw e;
	}

	return iFileSize;
}

// Remane the file
bool File::Rename(std::string strOldFilePath, std::string strNewFilePath)
{
	if (strOldFilePath.empty())
	{
		return false;
	}

	if (strNewFilePath.empty())
	{
		return false;
	}

	try
	{
		try
		{
			boost::filesystem::path CurSrcFilePath(strOldFilePath);

			boost::filesystem::path CurNewFilePath(strNewFilePath);

			boost::filesystem::rename(CurSrcFilePath, CurNewFilePath);
		}
		catch (boost::filesystem::filesystem_error & e)
		{
			throw BSException() << e.code().value() << e.what();
		}
	}
	catch (BSException & e)
	{
		throw e;
	}

	return true;
}

// Create new file recursively
bool File::Create(std::string strFilePath)
{
	if (!File().Open(strFilePath,FILE_WRITE))
	{
		return false;
	}

	return true;
}

// Copy the file recursively
bool File::Copy(std::string strSrcFilePath, std::string strDestFilePath, bool bIsCover)
{
	if (strSrcFilePath.empty())
	{
		return false;
	}

	if (strDestFilePath.empty())
	{
		return false;
	}

	try
	{
		try
		{
			boost::filesystem::path CurDestFilePath(strDestFilePath);

			std::string strParentDirPath = GetParentPath(strDestFilePath);

			if (!IsExisted(strParentDirPath))
			{
				if (!Directory::Create(strParentDirPath))
				{
					return false;
				}
			}

			boost::system::error_code ErrorCode;

			boost::filesystem::copy_option Option = boost::filesystem::copy_option::none;

			if (bIsCover)
			{
				Option = boost::filesystem::copy_option::overwrite_if_exists;
			}

			if (!boost::filesystem::copy_file(strSrcFilePath,
				strDestFilePath,
				Option,
				ErrorCode))
			{
				if (ErrorCode.value() == 0 || ErrorCode.value() == 80)
				{
					return true;
				}

				throw BSException() << ErrorCode.value() << ErrorCode.message();
			}
		}
		catch (boost::filesystem::filesystem_error & e)
		{
			throw BSException() << e.code().value() << e.what();
		}
	}
	catch (BSException & e)
	{
		throw e;
	}

	return true;
}

// Delete the file recursively
bool File::Delete(std::string strFilePath)
{
	if (strFilePath.empty())
	{
		return 0;
	}

	try
	{
		try
		{
			boost::filesystem::path CurFilePath(strFilePath);

			boost::system::error_code ErrorCode;

			if (!boost::filesystem::remove(CurFilePath, ErrorCode))
			{
				throw BSException() << ErrorCode.value() << ErrorCode.message();
			}
		}
		catch (boost::filesystem::filesystem_error & e)
		{
			throw BSException() << e.code().value() << e.what();
		}
	}
	catch (BSException & e)
	{
		throw e;
	}

	return true;
}

// Is the file existed or not
bool File::IsExisted(std::string strFilePath)
{
	if (strFilePath.empty())
	{
		return 0;
	}

	try
	{
		try
		{
			boost::filesystem::path CurFilePath(strFilePath);

			boost::system::error_code ErrorCode;

			if (!boost::filesystem::exists(CurFilePath, ErrorCode))
			{
				return false;
			}
		}
		catch (boost::filesystem::filesystem_error & e)
		{
			throw BSException() << e.code().value() << e.what();
		}
	}
	catch (BSException & e)
	{
		throw e;
	}

	return true;
}

// Get last wite time of file
std::time_t File::GetLastWriteTime(std::string strFilePath)
{
	if (strFilePath.empty())
	{
		return false;
	}

	time_t iLastWriteTime = 0;

	try
	{
		try
		{
			boost::filesystem::path CurDirPath(strFilePath);

			iLastWriteTime = boost::filesystem::last_write_time(CurDirPath);
		}
		catch (boost::filesystem::filesystem_error & e)
		{
			throw BSException() << e.code().value() << e.what();
		}
	}
	catch (BSException & e)
	{
		throw e;
	}

	return iLastWriteTime;
}

// Set last wite time of file
bool File::SetLastWriteTime(std::string strFilePath, std::time_t iNewTime)
{
	if (strFilePath.empty())
	{
		return false;
	}

	try
	{
		try
		{
			boost::filesystem::path CurDirPath(strFilePath);

			boost::filesystem::last_write_time(CurDirPath, iNewTime);
		}
		catch (boost::filesystem::filesystem_error & e)
		{
			throw BSException() << e.code().value() << e.what();
		}
	}
	catch (BSException & e)
	{
		throw e;
	}

	return true;
}

// Get rooth path of the file
std::string File::GetRootPath(std::string strFilePath)
{
	if (strFilePath.empty())
	{
		return "";
	}

	boost::filesystem::path CurDirPath(strFilePath);

	boost::filesystem::path RootPath = CurDirPath.root_path();

	return RootPath.string();
}

// Get parent path of the file
std::string File::GetParentPath(std::string strFilePath)
{
	if (strFilePath.empty())
	{
		return "";
	}

	boost::filesystem::path CurDirPath(strFilePath);

	boost::filesystem::path ParentPath = CurDirPath.parent_path();

	return ParentPath.string();
}
